﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Toolkit.Web.Resources;

namespace Toolkit.Web.Validation
{
    public class PostalCodeValidator
    {
        public PostalCodeValidator(string postalCode, string isoCountryCode2)
        {
            if (string.IsNullOrEmpty(isoCountryCode2))
            {
                throw new ArgumentException();
            }

            if (isoCountryCode2.Length != 2)
            {
                throw new ArgumentException("IsoCountryCode2 must be exactly 2 characters in length.");
            }

            PostalCode = postalCode;
            IsoCountryCode2 = isoCountryCode2;
        }

        public string PostalCode
        {
            get;
            private set;
        }

        public string IsoCountryCode2
        {
            get;
            private set;
        }

        /// <summary>
        /// Matches United States postal codes (4 digit format or 9 digit format). 
        /// Requires it to be in formats similar to ##### or #####-####.
        /// </summary>
        protected const string UnitedStatesPostalCode = @"^([0-9]{5})(?:[- ]{0,1}([0-9]{4}))?$";

        /// <summary>
        /// Matches Canadian postal codes with or without a space in the middle. Requires it to be 6 characters and in the format
        /// 'A#A #A#' where A is a letter and # is a digit.
        /// </summary>
        protected const string CanadaPostalCode = @"^([ABCEGHJKLMNPRSTVXYabceghjklmnprstvxy]{1}\d{1}[A-Za-z]{1})(?:[- ]{0,1})(\d{1}[A-Za-z]{1}\d{1})$";

        /// <summary>
        /// Matches Japan postal codes (7 numeric characters with a dash after the first 3 ###-####). 
        /// </summary>
        protected const string JapanPostalCode = @"^([0-9]{3})(?:[- ]{0,1})([0-9]{4})$";

        /// <summary>
        /// Matches Australian postal codes which are required to be 4 numeric characters.
        /// </summary>
        protected const string AustraliaPostalCode = @"^([0-9]{4})$";

        /// <summary>
        /// Matches German postal codes which are required to be 5 numeric characters.
        /// </summary>
        protected const string GermanyPostalCode = @"^([0-9]{5})$";

        /// <summary>
        /// Matches French postal codes which are required to be 5 numeric characters.
        /// </summary>
        protected const string FrancePostalCode = @"^([0-9]{5})$";

        public string Validate()
        {
            if (string.IsNullOrEmpty(PostalCode))
            {
                // Whether this value exists or not should be handled with a RequiredAttribute.
                return null;
            }

            PostalCode = PostalCode.Trim();

            switch (IsoCountryCode2.ToUpper())
            {
                // United States.
                case "US":
                    if (!Regex.IsMatch(PostalCode, UnitedStatesPostalCode))
                    {
                        return string.Format(Language.Validation_InvalidPostalCode_Format, string.Format("##### {0} #####-####", Language.Or.ToLower()));
                    }
                    break;

                // Canada.
                case "CA":
                    if (!Regex.IsMatch(PostalCode, CanadaPostalCode))
                    {
                        return string.Format(Language.Validation_InvalidPostalCode_Format, "A9A 9A9");
                    }
                    break;

                // Australia.
                case "AU":
                    if (!Regex.IsMatch(PostalCode, AustraliaPostalCode))
                    {
                        return string.Format(Language.Validation_InvalidPostalCode_Format, "####");
                    }
                    break;

                // Germany.
                case "DE":
                    if (!Regex.IsMatch(PostalCode, GermanyPostalCode))
                    {
                        return string.Format(Language.Validation_InvalidPostalCode_Format, "#####");
                    }
                    break;

                // France.
                case "FR":
                    if (!Regex.IsMatch(PostalCode, FrancePostalCode))
                    {
                        return string.Format(Language.Validation_InvalidPostalCode_Format, "#####");
                    }
                    break;

                // Japan.
                case "JP":
                    if (!Regex.IsMatch(PostalCode, JapanPostalCode))
                    {
                        return string.Format(Language.Validation_InvalidPostalCode_Format, "###-####");
                    }
                    break;
            }

            return null;
        }
    }
}
